package com.tangzc.mybatisflex.autotable;

import com.mybatisflex.annotation.Column;
import com.mybatisflex.annotation.EnumValue;
import com.mybatisflex.annotation.Id;
import com.mybatisflex.annotation.KeyType;
import com.mybatisflex.annotation.RelationManyToMany;
import com.mybatisflex.annotation.RelationManyToOne;
import com.mybatisflex.annotation.RelationOneToMany;
import com.mybatisflex.annotation.RelationOneToOne;
import com.mybatisflex.annotation.Table;
import com.mybatisflex.core.FlexGlobalConfig;
import com.mybatisflex.core.table.DynamicTableProcessor;
import com.mybatisflex.core.table.TableManager;
import com.mybatisflex.core.util.ClassUtil;
import com.mybatisflex.spring.boot.MybatisFlexProperties;
import com.tangzc.mybatisflex.util.StringHelper;
import org.dromara.autotable.core.AutoTableMetadataAdapter;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author don
 */
public class CustomAutoTableMetadataAdapter implements AutoTableMetadataAdapter {

    private final MybatisFlexProperties mybatisFlexProperties;

    public CustomAutoTableMetadataAdapter(MybatisFlexProperties mybatisFlexProperties) {
        this.mybatisFlexProperties = mybatisFlexProperties;
    }

    @Override
    public Boolean isIgnoreField(Field field, Class<?> clazz) {

        Column tableField = AnnotatedElementUtils.findMergedAnnotation(field, Column.class);
        if (tableField != null && tableField.ignore()) {
            return true;
        }
        RelationOneToOne relationOneToOne = AnnotatedElementUtils.findMergedAnnotation(field, RelationOneToOne.class);
        if (relationOneToOne != null) {
            return true;
        }
        RelationOneToMany relationOneToMany = AnnotatedElementUtils.findMergedAnnotation(field, RelationOneToMany.class);
        if (relationOneToMany != null) {
            return true;
        }
        RelationManyToOne relationManyToOne = AnnotatedElementUtils.findMergedAnnotation(field, RelationManyToOne.class);
        if (relationManyToOne != null) {
            return true;
        }
        RelationManyToMany relationManyToMany = AnnotatedElementUtils.findMergedAnnotation(field, RelationManyToMany.class);
        return relationManyToMany != null;
    }

    @Override
    public Boolean isPrimary(Field field, Class<?> clazz) {
        if (AnnotatedElementUtils.hasMetaAnnotationTypes(field, Id.class)) {
            return true;
        }

        return "id".equals(field.getName());
    }

    @Override
    public Boolean isAutoIncrement(Field field, Class<?> clazz) {

        if (!isPrimary(field, clazz)) {
            return false;
        }

        Id tableId = AnnotatedElementUtils.findMergedAnnotation(field, Id.class);
        if (tableId == null || tableId.keyType() == KeyType.None) {
            // 判断全局配置
            FlexGlobalConfig.KeyConfig keyConfig = FlexGlobalConfig.getDefaultConfig().getKeyConfig();
            return Optional.ofNullable(keyConfig)
                    .map(FlexGlobalConfig.KeyConfig::getKeyType)
                    .orElse(KeyType.None) == KeyType.Auto;
        } else {
            return tableId.keyType() == KeyType.Auto;
        }
    }

    @Override
    public List<String> getColumnEnumValues(Class<?> enumClassType) {
        if (enumClassType.isEnum()) {
            List<Field> enumDbValueFields = ClassUtil.getAllFields(enumClassType, f -> f.getAnnotation(EnumValue.class) != null);
            if (!enumDbValueFields.isEmpty()) {
                Field valField = enumDbValueFields.get(0);
                // 设置私有字段可访问
                valField.setAccessible(true);
                return Arrays.stream(enumClassType.getEnumConstants())
                        .map(enumConstant -> {
                            try {
                                return valField.get(enumConstant);
                            } catch (IllegalAccessException e) {
                                throw new RuntimeException(e);
                            }
                        })
                        .map(Objects::toString)
                        .collect(Collectors.toList());
            } else {
                return Arrays.stream(enumClassType.getEnumConstants())
                        .map(Object::toString)
                        .collect(Collectors.toList());
            }
        } else {
            throw new IllegalArgumentException(String.format("Class: %s 非枚举类型", enumClassType.getName()));
        }
    }

    @Override
    public String getTableName(Class<?> clazz) {

        String tableName = null;
        Table table = AnnotatedElementUtils.findMergedAnnotation(clazz, Table.class);
        if (table != null && StringUtils.hasText(table.value())) {
            tableName = table.value();
        } else {
            tableName = smartConvert(table, clazz.getSimpleName());
        }
        DynamicTableProcessor dynamicTableProcessor = TableManager.getDynamicTableProcessor();
        if (dynamicTableProcessor != null) {
            tableName = dynamicTableProcessor.process(tableName);
        }
        return tableName;
    }

    /**
     * 根据注解顺序和配置，获取字段对应的数据库字段名
     *
     * @param field
     * @return
     */
    @Override
    public String getColumnName(Class<?> clazz, Field field) {
        Column column = AnnotatedElementUtils.findMergedAnnotation(field, Column.class);
        if (column != null && StringUtils.hasText(column.value()) && !column.ignore()) {
            return filterSpecialChar(column.value());
        }

        Table table = AnnotatedElementUtils.findMergedAnnotation(clazz, Table.class);
        return smartConvert(table, field.getName());
    }

    @Override
    public String getTableComment(Class<?> clazz) {
        Table table = AnnotatedElementUtils.findMergedAnnotation(clazz, Table.class);
        if (table != null && StringUtils.hasText(table.comment())) {
            return table.comment();
        }
        return AutoTableMetadataAdapter.super.getTableComment(clazz);
    }

    @Override
    public String getTableSchema(Class<?> clazz) {
        Table table = AnnotatedElementUtils.findMergedAnnotation(clazz, Table.class);
        if (table != null && StringUtils.hasText(table.schema())) {
            return table.schema();
        }
        return AutoTableMetadataAdapter.super.getTableSchema(clazz);
    }

    @Override
    public String getColumnComment(Field field, Class<?> clazz) {
        Column column = AnnotatedElementUtils.findMergedAnnotation(clazz, Column.class);
        if (column != null && StringUtils.hasText(column.comment())) {
            return column.comment();
        }
        return AutoTableMetadataAdapter.super.getColumnComment(field, clazz);
    }

    private String filterSpecialChar(String name) {
        return name.replaceAll("`", "");
    }

    public String smartConvert(Table table, String column) {

        boolean camelToUnderline;
        if (table == null) {
            // 获取全局的配置
            MybatisFlexProperties.CoreConfiguration configuration = mybatisFlexProperties.getConfiguration();
            // 没有配置的话，默认true
            camelToUnderline = Optional.ofNullable(configuration).map(MybatisFlexProperties.CoreConfiguration::getMapUnderscoreToCamelCase).orElse(true);
        } else {
            // 表上单独开启字段下划线申明
            camelToUnderline = table.camelToUnderline();
        }
        // 全局开启字段下划线申明
        if (camelToUnderline) {
            column = StringHelper.camelToUnderline(column);
        }

        return column;
    }
}
